#include <vector>
#include <ext/hash_map>
#include <deque>
#include <numeric>
#include <algorithm>
#include <string>


#define DEBUG_LEVEL 8
#include <debug_func.h>
#include <ultoa.h>
#include <progress_indicator.h>
#include <tab_delimited_str.h>
#include <stlhelper.h>
#include <index_string.h>

//boost
#include <boost/foreach.hpp>
#include "data_input.h"

using std::cin;
using std::deque;
using std::cout;
using std::vector;
using std::string;
using std::sort;
using std::ostream;


//________________________________________________________________________________________

//	index_ortholog_id

//		convert external ortholog_id to my ortholog_id

//________________________________________________________________________________________
//
// t_hash_uu
// 
typedef __gnu_cxx::hash_map<unsigned, unsigned> t_hash_uu;
unsigned index_ortholog_id(t_hash_uu& 	ortholog_id_ext_to_ortholog_id, 
						   unsigned		ortholog_id_ext,
						   unsigned&	cnt_ortholog_sets)
{
    // make room for new cluster if necessary
    if (!ortholog_id_ext_to_ortholog_id.count(ortholog_id_ext))
    {
        ortholog_id_ext_to_ortholog_id[ortholog_id_ext] = cnt_ortholog_sets;
        return cnt_ortholog_sets++;
    }
    return ortholog_id_ext_to_ortholog_id[ortholog_id_ext];

}


//________________________________________________________________________________________

//	t_temp_ortholog_info

//		input data in raw form before sorting and relative gene positions calculated

//________________________________________________________________________________________
struct t_temp_ortholog_info
{
	t_temp_ortholog_info(	unsigned	ortholog_id_,
							unsigned	gene_id_,
							float		gene_pos_ext_,
							bool		strand_):
									ortholog_id	(ortholog_id_),
									gene_id     (gene_id_),
									gene_pos_ext(gene_pos_ext_),
									strand      (strand_)
	{
	}
	bool operator < (const t_temp_ortholog_info& other) const
	{	return gene_pos_ext < other.gene_pos_ext;	}
	unsigned	ortholog_id;
	unsigned	gene_id;
	float		gene_pos_ext;
	bool		strand;
};


void add_temp_ortholog_info(unsigned							species_id, 
							unsigned							chromosome_id, 
							unsigned							ortholog_id, 
							unsigned							gene_id, 
							float								gene_pos_ext, 
							bool 								strand,
							deque<deque<t_temp_ortholog_info> >	temp_ortholog_info[])
{
	// make sure memory allocated for chromosome for this species
	if (temp_ortholog_info[species_id].size() <= chromosome_id)
		temp_ortholog_info[species_id].push_back(deque<t_temp_ortholog_info>());

	temp_ortholog_info[species_id][chromosome_id].push_back(
					t_temp_ortholog_info(ortholog_id, gene_id, gene_pos_ext, strand));
}



//________________________________________________________________________________________

//	read_gene_loci_data

//		read data from stream

//________________________________________________________________________________________
void read_gene_loci_data(deque<deque<unsigned> >	gene_pos_by_chrm[],
						 vector<char>&				gene_pos_to_orphan,
						 vector<t_strand>&			gene_pos_to_strand,
						 vector<unsigned>&			gene_pos_to_gene_id,
						 vector<float>&				gene_pos_to_geneposext,
						 vector<unsigned>&			gene_pos_to_chromosome,
						 vector<unsigned >&			gene_pos_to_ortholog_id,     
						 t_index_string&			gene_names,
						 t_index_string& 			species_names,
						 t_index_string 			chromosome_names[],
						 deque<deque<unsigned> >&   ortholog_sets,           
						 ostream&					VERBOSE
						 )
{
	debug_func_cout(5);
	// dictionary of ortholog_ids and external tokens
	t_hash_uu 	ortholog_id_ext_to_ortholog_id_common;



	// save all ortholog information before sorting
	deque<deque<t_temp_ortholog_info> > temp_ortholog_info[2];
	unsigned 							cnt_ortholog_sets = 0;
	

	// read species, chromosome, ortholog_id, gene_position, strand, gene_id
	unsigned cnt_lines = 0;
	{
		t_progress_indicator dots(VERBOSE,  1000);
		while (cin.good())
		{
			++cnt_lines;
			++dots;
			//
			//	Species: make sure only two
            //
            char endc;
			string species_name = read_char_delimited_str(cin, '\n', endc);
			if (!cin.good())
				break;
			// make sure species name ends on a tab
			if (endc != '\t')
				throw std::runtime_error("Unrecognised format on line #" + ultoa(cnt_lines));
			unsigned species_id		= species_names.index(species_name);
			if (species_names.size() > 2)
				throw std::logic_error(
					string("Genes from more than 3 species were supplied: ([") +
							species_names.string_at(0) + "], [" +
							species_names.string_at(1) + "], [" +
							species_names.string_at(2) + "])");
	
	
			
			//
			//	chromosome && Species
			//
			unsigned chromosome_id	= chromosome_names[species_id].index(
												read_char_delimited_str(cin, '\t', endc));
			if (endc != '\t')
				throw std::runtime_error("Unrecognised format on line #" + ultoa(cnt_lines));
	
	
			//
			//	ortholog_id
			//
			unsigned ortholog_id_ext;
			cin >> ortholog_id_ext;
			unsigned ortholog_id = index_ortholog_id(ortholog_id_ext_to_ortholog_id_common, 
													 ortholog_id_ext,
													 cnt_ortholog_sets);
			
			//
			//	gene_position and strand and gene_id
			//
			float		gene_pos_ext;
			bool		strand;
						cin >> gene_pos_ext >> strand;
			unsigned	gene_id = gene_names.index(read_char_delimited_str(cin, '\t', endc));




			//	
			//	save all
			//  
			add_temp_ortholog_info(species_id, chromosome_id, ortholog_id, gene_id, 
								   gene_pos_ext, strand, temp_ortholog_info);
		}
	}
	if (!cin.eof())
		throw std::runtime_error("Parsing error on line " + ultoa(cnt_lines));
	

	if (species_names.size() < 2)
		throw std::logic_error(
								string("Genes from only 1 species (") +
										species_names.string_at(0) + ") were supplied.");


	//
	//	sort temp_ortholog_info by position
	//
	for (unsigned species_id = 0; species_id < 2; ++species_id)
		BOOST_FOREACH(deque<t_temp_ortholog_info>& chromosome, temp_ortholog_info[species_id])
			sort(chromosome.begin(), chromosome.end());

	

	unsigned gene_pos = 0;
	
	// first ortholog_sets is empty: used as a null value
	// 1 ortholog_set per original ortholog_id per species, hence * 2 + 1
	ortholog_sets.assign(cnt_ortholog_sets * 2 + 1, deque<unsigned>());

	
	{
		t_progress_indicator dots(VERBOSE,  1000);
		for (unsigned species_id = 0; species_id < 2; ++species_id)
		{
			// assign memory for all chromosomes
			gene_pos_by_chrm[species_id].insert(gene_pos_by_chrm[species_id].end(), 
												chromosome_names[species_id].size(),
												deque<unsigned>());
			// increment gene_count for all
			for (unsigned chrm_id = 0; chrm_id < chromosome_names[species_id].size(); ++chrm_id)
			{
				BOOST_FOREACH(t_temp_ortholog_info& info, temp_ortholog_info[species_id][chrm_id])
				{
					// lookups for all
					gene_pos_by_chrm[species_id][chrm_id].push_back(gene_pos);
					gene_pos_to_chromosome.push_back(chrm_id);
					gene_pos_to_strand.push_back(info.strand ? 1 : -1);
					gene_pos_to_gene_id.push_back(info.gene_id);
					gene_pos_to_geneposext.push_back(info.gene_pos_ext);
					ortholog_sets[info.ortholog_id * 2 + species_id + 1].push_back(gene_pos);
					#ifndef NDEBUG
					cout << "species = " << species_id
						 << ", gene_id = " << gene_names[info.gene_id]
						 << ", genepos = " << gene_pos
						 << " <= " << info.gene_pos_ext << "\n";
					#endif
					++gene_pos;
					++dots;
				}
			}
		}
	}

	gene_pos_to_orphan.assign(gene_pos, 0);

	// genes without orthologs will have a ortholog_id of zero
	// the ortholog set 0 is empty
	gene_pos_to_ortholog_id.assign(gene_pos, 0);
	assert(ortholog_sets[0].empty());

	//
	//	gene_pos_to_ortholog_gene_pos_lookup
	//
	{
		t_progress_indicator dots(VERBOSE,  1000);
		for (unsigned ortholog_id = 0; ortholog_id < cnt_ortholog_sets; ++ortholog_id)
		{
			// link the gene pos of one species to the orthologs of the other
			unsigned ortholog_id0 = ortholog_id * 2 + 1;
			unsigned ortholog_id1 = ortholog_id * 2 + 2;
			BOOST_FOREACH(unsigned gene_pos, ortholog_sets[ortholog_id0])
				gene_pos_to_ortholog_id[gene_pos] = ortholog_id1;
			BOOST_FOREACH(unsigned gene_pos, ortholog_sets[ortholog_id1])
				gene_pos_to_ortholog_id[gene_pos] = ortholog_id0;
		}
	}
}



//
// 
// 
//	Data input
//
//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888



